X1-3:从记忆到认知
Easy Data x AI 课程 · 扩展篇 · 下篇
【上篇】和中篇解决了单 Agent 生命周期与多 Agent 边界。下篇回答:记忆堆多了之后,怎么蒸馏出稳定认知?怎么知道系统记得好不好?
开篇
【上篇】让单 Agent 会记、会忘、会裁决冲突;中篇划清了多 Agent 之间的隔离与共享。到这里,记忆系统已经能稳定运转。但条目一多,新问题浮现:
- 该永久保留什么? 原始对话越堆越多,Prompt 里塞不下,也不该全塞
- 弱信号怎么升华? 单条不重要,多条拼起来却是重要事实(在【上篇】中提到的被动巩固的盲区)
- 怎么证明记得好? 衰减参数、冲突策略调完,凭什么说比 Mem0 / 全量上下文更好
下篇用 code/X1/part3_consolidation/ 的示例代码,串成两条线:巩固(怎么从海量条目里提纯认知)→ 评测(怎么量化质量并选型)。
目录
第一部分:永久记忆意味着什么
上下文窗口涨到 1M tokens 之后,常见疑问是:直接把历史对话全塞进去不就行了?但实际上不是这样的。
有三个原因让"全量塞窗口"在工程上不可持续:
- 成本:注意力随长度近似平方增长,百万 token 单次推理成本是万级的上百倍
- 精度:lost-in-the-middle 现象下,窗口越大,中间信息越容易被漏掉;语义检索 + 衰减精排往往更准
- 数据主权:窗口里的数据随会话消失,无法独立管理、审计、删除;记忆库才是持久层
记忆不是窗口的临时替代品,而是独立的基础设施。 下篇讨论的"永久记忆",也不是永不删除,而是:
进入衰减极慢的稳定层 + 具备蒸馏为更高阶结构(画像、经验、Skill)的能力
【上篇】讲过 working / short_term / long_term 三层和层级倍率。这里从认知角度再看一遍筛选链路:
新信息 → working(快速淘汰)→ short_term(近期信号)→ long_term(稳定认知,可蒸馏)| 层级 | 承接量 | 职责 |
|---|---|---|
| working | 百条/会话级 | 承接原始输入 |
| short_term | 数十条 | 保留近期有价值信号 |
| long_term | 十几条核心 | 存储稳定认知,可写入画像 |
目标不是存更多,而是淘汰不重要的、提纯重要的。 D4 讲过 CoALA 的三种长期记忆(语义、情景、程序),工程上存储方式差异很大:语义走向量检索,程序是 System Prompt,情景常以 few-shot 注入。第三节会专门讨论怎么把"确定性画像"和"长尾事实"分开存。
第二部分:三种巩固触发方式
一条记忆从临时印象变成长期认知,需要巩固(Consolidation)。在【上篇】的 2.1 节的被动巩固(写入时按 importance 分层)只覆盖一种触发;示例代码演示三种方式,对应不同业务节奏。
2.1 被动触发:写入时的一次分流
【上篇】已讲过 classify_memory_type:importance ≥ 0.8 进 long_term,0.6~0.8 进 short_term,其余进 working。x1_11_consolidation_passive.py 用 6 条记忆验证分层与 30 天后
| 内容 | 重要性 | 层级 | 30 天后 |
|---|---|---|---|
| 对花生过敏 | 0.95 | long_term | > 0.7 |
| 主力 Go,8 年经验 | 0.85 | long_term | > 0.7 |
| 电商项目 Django + PG | 0.72 | short_term | 中等 |
| 偏好简洁代码风格 | 0.65 | short_term | 中等 |
| 考虑学 Kubernetes | 0.55 | working | 接近 0 |
| 今天早上喝了咖啡 | 0.20 | working | 接近 0 |
优点:写入瞬间完成,零额外 LLM 成本。 局限:只看单条,发现不了"连续三周搜 Rust 教程"这种跨条目模式。
2.2 主动触发:Reflection 蒸馏
【上篇】 2.1 末尾留过这个坑:多条 short_term 拼在一起,可能隐含一个 stable 事实。这就是主动触发(Reflection):周期性扫描近期记忆,按话题分组后让 LLM 判断能否蒸馏。
x1_12_consolidation_reflection.py 读取 fixtures/reflection_input.json(10 条记忆,4 个话题)。以 Rust 学习为例,5 条单独看 importance 都不高:
- 用户在看 Rust 教程
- 用户问了生命周期问题
- 用户搭建了 Rust 开发环境
- 用户在使用 Axum 框架
- 用户遇到 ownership 问题
放在一起,可以蒸馏为:
用户正在系统学习 Rust,已搭建开发环境并使用 Axum 实践,目前处于初学者阶段
def reflection_analyze(topic, memories, min_count=3, min_confidence=0.7):
"""按话题扫描近期记忆,蒸馏为稳定事实(生产环境由 LLM 完成)"""
if len(memories) < min_count:
return None # 证据不足,继续在中期层等待
distilled_fact = llm_summarize(topic, memories)
confidence = llm_confidence(topic, memories)
return {
"distilled_fact": distilled_fact,
"confidence": confidence,
"action": "consolidate_to_long_term"
if confidence >= min_confidence
else "keep_in_short_term",
}| 参数 | 含义 | 为什么需要 |
|---|---|---|
min_count | 至少几条独立记忆才触发 | 避免单条孤证直接入 long_term |
min_confidence | 置信度阈值 | 不达标则保留在 short_term,等待更多证据 |
2.3 懒惰触发:检索达阈值再总结
第三种是懒惰触发:某类记忆被检索
| 触发方式 | 时机 | 成本 | 适用 |
|---|---|---|---|
| 被动 | 写入瞬间 | 最低 | 过敏、职业等单条高重要性事实 |
| 主动 | 周期性扫描 | 中等 | 多条弱信号拼成强事实 |
| 懒惰 | 检索达阈值 | 按需 | 偏好类、高频查询话题 |
三种方式互补:【上篇】解决单条怎么衰减,本节解决多条怎么升华。
动手实验 1~2:巩固链路
cd code/X1/part3_consolidation
python x1_11_consolidation_passive.py
python x1_12_consolidation_reflection.py跑 x1_12 时重点看:哪些话题因 min_count 或 min_confidence 未达标而留在中期层?蒸馏后长期事实条数 vs 原始记忆条数的压缩比是多少?
第三部分:画像与事实库为什么要分开
巩固之后,长期认知有两种典型形态:结构化画像(姓名、主力语言、云平台)和长尾事实(某次用 Django 处理百万行 CSV 的细节)。混在一起存,检索时容易两头吃亏。
| 用户画像 | 向量事实库 | |
|---|---|---|
| 变化频度 | 慢(几周才变) | 快(每次对话都可能写入) |
| 更新方式 | 覆盖式 | 增量 append |
| 查询方式 | 按 key 精确读取 | 语义模糊匹配 |
| 条目数 | 固定几十个字段 | 无上限 |
| 典型内容 | 技术栈、角色、核心偏好 | 项目细节、对话上下文 |
x1_13_profile_vs_facts.py 对比三种检索方式,并演示组合策略:先画像、后事实库:
def query_combined(profile, facts, query):
"""画像给确定性约束,事实库补长尾"""
# 路径 1:按 key 精确匹配,"用户用 Go" 不会被语义搜索漏掉
deterministic = {}
for key, value in flatten(profile).items():
if keyword_match(key, query) or keyword_match(value, query):
deterministic[key] = value
# 路径 2:事实库语义搜索,覆盖画像字段以外的细节
contextual = semantic_search(query, facts, top_n=5)
return {"deterministic": deterministic, "contextual": contextual}用户问"推荐后端技术方案"时,画像路径返回 primary_language: Go、cloud: 阿里云;事实库路径补充"曾用 Django 处理百万行 CSV"这类长尾。两者进 Prompt,LLM 既不会漏核心约束,也不会只剩干巴巴的几个字段。
动手实验 3:三种检索方式对比
python x1_13_profile_vs_facts.py对照输出:仅画像、仅事实库、组合检索在同一 query 下差在哪?有没有"仅事实库"时漏掉 primary_language 的情况?
第四部分:从经验到 Skill
巩固解决"记什么",经验蒸馏解决"记了怎么用"。多轮成功交互可结构化为三元组:
(问题情境, 采取方案, 结果)x1_14_experience_distill.py 演示三步:extract_experience_triples → cluster_experiences → 外化为 Skill 描述。
from collections import defaultdict
def extract_experience_triples(episodic_memories):
"""从情景记忆提取 (问题, 方案, 结果)"""
triples = []
for mem in episodic_memories:
triples.append({
"problem": mem["problem"],
"solution": mem["solution"],
"result": mem["result"],
"domain": mem["domain"],
"success": "成功" in mem["result"],
})
return triples
def cluster_experiences(triples):
"""同类成功经验聚类,归纳为可复用模式(生产环境由 LLM 完成)"""
by_domain = defaultdict(list)
for t in triples:
if t["success"]:
by_domain[t["domain"]].append(t)
patterns = {}
for domain, exp_list in by_domain.items():
if len(exp_list) >= 2:
patterns[domain] = induce_pattern(domain, exp_list)
return patterns两条"数据处理"成功经验可能归纳为:"处理大文件时,优先推荐 Python/pandas 的 chunked reading"。置信度达标后,可外化为 Skill 规则注入 System Prompt。P4 讨论的 Skill 知识管理中,一部分规则就来自这条路径:记忆系统输出经验,Skill 系统输出行为规则。
动手实验 4:经验到 Skill 雏形
python x1_14_experience_distill.py看输出里哪些 domain 聚类成功、哪些因样本不足未形成模式。Think:这些 Skill 雏形若写入 System Prompt,Agent 行为会和"仅检索记忆"有何不同?
第五部分:评测与选型
巩固和架构设计回答"怎么记好"。上线前还要回答:怎么证明记得好? 否则
5.1 公开基准在测什么
| 基准 | 侧重 | 关键结论 |
|---|---|---|
| LOCOMO | 长期对话 + 时序推理 | 有选择记忆 + 衰减(78.7%)> 全量记住(52.9%) |
| MemoryAgentBench | 事实、偏好更新、冲突、遗忘 | 多跳冲突和遗忘是共同短板 |
| MemoryArena | 记忆驱动行动 | 光 recall 不够,须影响 Agent 决策 |
| AppWorld | 长程任务 | 有记忆管理在 Token 和通过率上均优于无记忆 |
LOCOMO 的结论与【上篇】呼应:不是记得越多越好。 有选择地记 + 主动忘,反而比全量堆窗口准确。
5.2 不止看准确率
| 维度 | 指标 | 为什么重要 |
|---|---|---|
| 准确性 | 事实召回率、冲突裁决正确率 | 基本功 |
| 效率 | Token 消耗、检索 P99 | 20 条 × 200 tokens = 4000 tokens/对话 |
| 成本 | 每千条存储空间 | 向量索引长期运营成本 |
| 鲁棒性 | 冲突可逆性、隔离泄漏率 | 中篇 scope 设计是否有效 |
| 一致性 | 同一 query 两次回答方差 | 【上篇】确定性聚合是否生效 |
x1_15_benchmark_runner.py 在同一套记忆和 QA 对上,切换不同
def run_benchmark(memories, qa_pairs, decay_rate, strategy_name):
"""对比不同衰减策略的准确率、Token、延迟"""
correct, total_tokens, latencies = 0, 0, []
for q in qa_pairs:
results, tokens, latency = search_memories(
memories, q["question"], decay_rate
)
if evaluate_answer(results, q["expected_answer"]):
correct += 1
total_tokens += tokens
latencies.append(latency)
p99_idx = int(len(latencies) * 0.99) if latencies else 0
return {
"strategy": strategy_name,
"decay_rate": decay_rate,
"accuracy": correct / len(qa_pairs),
"avg_tokens_per_query": total_tokens / len(qa_pairs),
"p99_latency_ms": sorted(latencies)[p99_idx] if latencies else 0,
}输入来自 fixtures/eval_qa_pairs.json。同一套数据、换
5.3 行业方案怎么选
| 类型 | 代表 | 核心思路 | 适合 |
|---|---|---|---|
| 托管 API | Mem0 | LLM 自动管理生命周期 | 快速接入 |
| 时序知识图谱 | Zep / Graphiti | 关系 + 时间显式建模 | 需要历史追溯 |
| Agent 自编辑 | Letta | Agent 自主读写记忆块 | 长程自主 Agent |
| 本地优先 | PowerMem | 混合检索 + 衰减 + 本地存储 | 数据主权敏感 |
建议流程:整理 50~100 条业务 QA → 在 LOCOMO 子集或自建集上跑分 → 准确率达标后,选 Token + 延迟 + 成本最低的方案。
动手实验 5:跑一遍简易评测
python x1_15_benchmark_runner.py对照不同
总结与参考资料
X1 系列三篇形成完整逻辑链:
| 篇 | 核心问题 | 关键机制 |
|---|---|---|
| 上篇 | 单 Agent 怎么记好 | 衰减 → 分层 → 检索精排 → 冲突裁决 → 聚合 |
| 中篇 | 多 Agent 边界在哪 | 隔离键 → 作用域 → promote → 信任 API |
| 下篇 | 怎么蒸馏认知、怎么量化 | 三种巩固 → 画像/事实分离 → 经验→Skill → 评测 |
下篇四条要点:
- 永久记忆 = 稳定层 + 蒸馏能力,不是无限堆原始对话
- 画像和事实库分离:确定性走精确读取,长尾走向量搜索
- 记忆 → 经验 → Skill 形成闭环,衔接 P4 Skill 体系
- 选型先定指标再跑分,功能清单不能代替 LOCOMO 或自建 QA 集
参考资料:
- D4 Agent 开发与记忆系统
- CoALA — arxiv.org/abs/2309.02427
- LOCOMO Benchmark — github.com/Shopify/locomo
- P4 Skill 与 Agent 知识管理
